home *** CD-ROM | disk | FTP | other *** search
/ Night Owl 6 / Night Owl's Shareware - PDSI-006 - Night Owl Corp (1990).iso / 019a / tde10src.zip / FINDREP.C < prev    next >
C/C++ Source or Header  |  1991-06-05  |  28KB  |  943 lines

  1. /*******************  start of original comments  ********************/
  2. /*
  3.  * Written by Douglas Thomson (1989/1990)
  4.  *
  5.  * This source code is released into the public domain.
  6.  */
  7.  
  8. /*
  9.  * Name:    dte - Doug's Text Editor program - find/replace module
  10.  * Purpose: This file contains the functions relating to finding text
  11.  *           and replacing text.
  12.  *          It also contains the code for moving the cursor to various
  13.  *           other positions in the file.
  14.  * File:    findrep.c
  15.  * Author:  Douglas Thomson
  16.  * System:  this file is intended to be system-independent
  17.  * Date:    October 1, 1989
  18.  */
  19. /*********************  end of original comments   ********************/
  20.  
  21. /*
  22.  * The search and replace routines have been EXTENSIVELY rewritten.
  23.  * The "brute force" search algorithm has been replaced by the Boyer-Moore
  24.  * text search algorithm.  This search should speed up search and replace
  25.  * operations.  Replace and search prompting has been changed.
  26.  *
  27.  * I am not very fond of the Wordstar/TURBO type search and replace prompting.
  28.  * Once the search pattern has been defined, one only needs to press a key
  29.  * to search forwards or backwards.  The forward or backward search key may
  30.  * be pressed at any time in any file once the pattern has been entered.
  31.  *
  32.  * New editor name:  tde, the Thomson-Davis Editor.
  33.  * Author:           Frank Davis
  34.  * Date:             June 5, 1991
  35.  *
  36.  * This modification of Douglas Thomson's code is released into the
  37.  * public domain, Frank Davis.   You may distribute it freely.
  38.  */
  39.  
  40. #include "tdestr.h"
  41. #include "common.h"
  42. #include "tdefunc.h"
  43.  
  44. /*
  45.  * Name:    get_replacement_flags
  46.  * Purpose: To input find and replace flags.
  47.  * Date:    June 5, 1991
  48.  * Passed:  line:  prompt line
  49.  * Returns: OK if flags were entered, ERROR if user wanted to abort
  50.  */
  51. int  get_replacement_flags( line )
  52. int line;
  53. {
  54. char line_buff[(MAX_COLS+1)*2]; /* buffer for char and attribute  */
  55. char *find_opt = "Options  (P)rompt before replace   (N)o prompt: ";
  56. int len, col, flash, normal, c, rc;
  57.  
  58.    len = strlen( find_opt );
  59.    col = len;
  60.    flash = g_display.message_color;
  61.    normal = g_display.text_color;
  62.    save_screen_line( 0, line, line_buff );
  63.    s_output( find_opt, line, 0, flash );
  64.    eol_clear( col, line, normal );
  65.    col += 2;
  66.    xygoto( col, line );
  67.    c = (c=getch()) != 0 ? c : getch() | 0x100;
  68.    while (c != 'P' && c != 'p' && c != 'N' && c != 'n' &&
  69.       c != ESC && c != RTURN)
  70.       c = (c=getch()) != 0 ? c : getch() | 0x100;
  71.    restore_screen_line( 0, line, line_buff );
  72.    switch (c) {
  73.       case 'P' :
  74.       case 'p' :
  75.       case RTURN :
  76.          g_status.replace_flag = PROMPT;
  77.          rc = OK;
  78.          break;
  79.       case 'N' :
  80.       case 'n' :
  81.          g_status.replace_flag = NOPROMPT;
  82.          rc = OK;
  83.          break;
  84.       default :
  85.          rc = ERROR;
  86.    }
  87.    bm.search_defined = rc;
  88.    return( rc );
  89. }
  90.  
  91.  
  92. /*
  93.  * Name:    ask_replace
  94.  * Purpose: Ask user if he want to (r)place, (s)kip, or (e)xit.
  95.  * Date:    June 5, 1991
  96.  * Returns: TRUE if user wants to replace, ERROR otherwise.
  97.  * Passed:  window: information allowing access to current window etc
  98.  *          finished: TRUE if user pressed ESC or (e)xit.
  99.  */
  100. int  ask_replace( window, finished )
  101. windows *window;
  102. int *finished;
  103. {
  104. char line_buff[(MAX_COLS+1)*2]; /* buffer for char and attribute  */
  105. char *ask_opt = "(R)eplace  (S)kip  (E)xit";
  106. int col, flash, normal, c, rc, prompt_line;
  107.  
  108.    prompt_line = window->cline - 1;
  109.    col = 39 - (strlen( ask_opt ) >> 1);
  110.    flash = g_display.message_color;
  111.    normal = g_display.text_color;
  112.    save_screen_line( 0, prompt_line, line_buff );
  113.    s_output( ask_opt, prompt_line, col, flash );
  114.    c = (c=getch()) != 0 ? c : getch() | 0x100;
  115.    while (c != 'R' && c != 'r' && c != 'S' && c != 's' &&
  116.           c != 'E' && c != 'e' && c != ESC)
  117.       c = (c=getch()) != 0 ? c : getch() | 0x100;
  118.    restore_screen_line( 0, prompt_line, line_buff );
  119.    switch (c) {
  120.       case 'R' :
  121.       case 'r' :
  122.          rc = OK;
  123.          break;
  124.       case 'E' :
  125.       case 'e' :
  126.       case ESC :
  127.          *finished = TRUE;
  128.       case 'S' :
  129.       case 's' :
  130.          rc = ERROR;
  131.          break;
  132.    }
  133.    return( rc );
  134. }
  135.  
  136.  
  137. /*
  138.  * Name:    do_replace
  139.  * Purpose: To replace text once match has been found.
  140.  * Date:    June 5, 1991
  141.  * Passed:  window: information allowing access to current window etc
  142.  *          start:  location of start of matched text
  143.  */
  144. void do_replace( window, start, direction )
  145. windows *window;
  146. text_ptr start;
  147. int direction;
  148. {
  149. int old_len;             /* length of original text */
  150. int new_len;             /* length of replacement text */
  151. int net_change;
  152. text_ptr source;         /* source of block move */
  153. text_ptr dest;           /* destination of block move */
  154. long number;             /* number of characters moved */
  155. windows *above;             /* window above current */
  156. windows *below;             /* window below current */
  157.  
  158.    old_len = strlen( g_status.pattern );
  159.    new_len = strlen( g_status.subst );
  160.  
  161.    /*
  162.     * move the text to either make room for the extra replacement text
  163.     *  or to close up the gap left
  164.     */
  165.    source = start + old_len;
  166.    dest = start + new_len;
  167.    number = ptoul( g_status.end_mem ) - ptoul( source );
  168.    hw_move( dest, source, number );
  169.  
  170.    /*
  171.     * insert the replacement text
  172.     */
  173.    for (dest=start, source=g_status.subst; *source;)
  174.       *dest++ = *source++;
  175.  
  176.    net_change = new_len - old_len;
  177.    adjust_start_end( window->file_info, net_change );
  178.    if (direction == FORWARD)
  179.       window->rcol += net_change;
  180.    adjust_windows_cursor( window, (long)net_change, 0 );
  181.    g_status.end_mem = addltop( (long)net_change, g_status.end_mem );
  182.    show_avail_mem( );
  183.    above = below = window;
  184.    while (above->prev || below->next) {
  185.       if (above->prev) {
  186.          above = above->prev;
  187.          if (window->file_info == above->file_info)
  188.             if (window->rline < above->rline)
  189.                above->cursor = addltop( (long)net_change, above->cursor );
  190.       } else if (below->next) {
  191.          below = below->next;
  192.          if (window->file_info == below->file_info)
  193.             if (window->rline < below->rline)
  194.                below->cursor = addltop( (long)net_change, below->cursor );
  195.       }
  196.    }
  197. }
  198.  
  199.  
  200. /*
  201.  * Name:    find_string
  202.  * Purpose: To set up and perform a find operation.
  203.  * Date:    June 5, 1991
  204.  * Passed:  window: information allowing access to current window etc
  205.  *          direction: find FORWARD or BACKWARD - determined by pressed key
  206.  *          new_string: search for new string to be entered by user
  207.  * Notes:   If user presses search string key then keep boyer-moore stuff.
  208.  *          To find the next occurence either forwards or backwards all the
  209.  *          user has to do is press the "RepeatFindForward" or
  210.  *          "RepeatFindBackward" key at any time.
  211.  */
  212. void find_string( window, direction, new_string )
  213. windows *window;
  214. int direction;
  215. int new_string;
  216. {
  217. char pattern[MAX_COLS];  /* text to be found */
  218. int rcol, prompt_line;
  219. long pattern_line, rline, test_line;
  220. text_ptr pattern_location;
  221. text_ptr p, q;
  222. char *snf = "string not found";
  223.  
  224.    prompt_line = window->bottom_line;
  225.    /*
  226.     * get search text, using previous as default
  227.     */
  228.    if (new_string == TRUE) {
  229.       strcpy( pattern, bm.pattern );
  230.       if (get_name( "String to find: ", prompt_line, pattern,
  231.                      g_display.message_color ) != OK)
  232.          return;
  233.       bm.search_defined = OK;
  234.       strcpy( bm.pattern, pattern );
  235.  
  236.       build_boyer_array( );
  237.    }
  238.  
  239.    if (bm.search_defined == OK) {
  240.       if (direction == FORWARD) {
  241.          if ((pattern_location = forward_boyer_moore_search( window )) != NULL)
  242.             find_adjust( window, pattern_location );
  243.          else
  244.             error( WARNING, prompt_line, snf );
  245.       } else {
  246.          if ((pattern_location = backward_boyer_moore_search( window )) != NULL)
  247.             find_adjust( window, pattern_location );
  248.          else
  249.             error( WARNING, prompt_line, snf );
  250.       }
  251.    } else
  252.       error( WARNING, prompt_line, "find pattern not defined" );
  253. }
  254.  
  255.  
  256. /*
  257.  * Name:    build_boyer_array
  258.  * Purpose: To set up the boyer array for foward and backward searches.
  259.  * Date:    June 5, 1991
  260.  * Notes:   Set up one array for forward searches and another for backward
  261.  *          searches.  If user decides to ignore case then fill in array
  262.  *          with reverse case characters.
  263.  */
  264. void build_boyer_array( )
  265. {
  266. int i, pl;
  267. char *p;
  268.  
  269.    /*
  270.     * set up for forward search
  271.     */
  272.    i = pl = bm.pattern_length = strlen( bm.pattern );
  273.  
  274.    /*
  275.     * set skip index of all characters to length of string
  276.     */
  277.    memset( bm.skip_forward, i, 256 );
  278.    i--;
  279.  
  280.    /*
  281.     * for each character in string, set the skip index
  282.     */
  283.    for (p=bm.pattern; i>=0; i--, p++)
  284.       bm.skip_forward[*p] = i;
  285.    if (bm.search_case == IGNORE) {
  286.       i = pl - 1;
  287.       for (p=bm.pattern; i>=0; i--, p++) {
  288.          if (*p >= 'A' && *p <= 'Z')
  289.             bm.skip_forward[*p+32] = i;
  290.          else if (*p >= 'a' && *p <= 'z')
  291.             bm.skip_forward[*p-32] = i;
  292.       }
  293.    }
  294.  
  295.    /*
  296.     * set up for backward search
  297.     */
  298.    i = -pl;
  299.    memset( bm.skip_backward, i, 256 );
  300.    i++;
  301.    for (p=bm.pattern+pl-1; i<=0; i++, p--)
  302.       bm.skip_backward[*p] = i;
  303.    if (bm.search_case == IGNORE) {
  304.       i = -pl + 1;
  305.       for (p=bm.pattern+pl-1; i<=0; i++, p--) {
  306.          if (*p >= 'A' && *p <= 'Z')
  307.             bm.skip_backward[*p+32] = i;
  308.          else if (*p >= 'a' && *p <= 'z')
  309.             bm.skip_backward[*p-32] = i;
  310.       }
  311.    }
  312. }
  313.  
  314.  
  315. /*
  316.  * Name:    foward_boyer_moore_search
  317.  * Purpose: search forward for pattern using boyer array
  318.  * Passed:  window: information allowing access to current window etc
  319.  * Returns: position in file if found else return NULL
  320.  * Date:    June 5, 1991
  321.  * Notes:   Start searching from cursor position to end of file.  If we hit
  322.  *          end of file without a match, start searching from the beginning
  323.  *          of file to cursor position.  (do wrapped searches)
  324.  */
  325. text_ptr forward_boyer_moore_search( window )
  326. windows *window;
  327. {
  328. int i, pl;
  329. text_ptr p, q, s, search_start;
  330. long search_length;
  331.  
  332.    /*
  333.     * if cursor is beyond end of line then start at end of line
  334.     */
  335.    window->cursor = cpf( window->cursor );
  336.    search_start = window->cursor;
  337.    i = window->rcol + 1;
  338.    pl = linelen( window->cursor );
  339.    if (i > pl)
  340.       i = pl;
  341.  
  342.    /*
  343.     * make start of search 1 character greater than current position so
  344.     * we don't repeatedly find the pattern at current position.
  345.     */
  346.    search_start += i;
  347.    s = search_start;
  348.    pl = bm.pattern_length;
  349.    i = pl - 1;
  350.  
  351.    /*
  352.     * find out how many character to search.  do standard Boyer-Moore search
  353.     */
  354.    search_length = ptoul( window->file_info->end_text ) - ptoul( s );
  355.    p = bm.pattern;
  356.    for (search_length -= i; search_length >= 0; search_length -= i) {
  357.       s = s + i;
  358.       i = bm.skip_forward[*s];
  359.       if (i == 0) {
  360.          q = s + 1 - pl;
  361.          q = cpf( q );
  362.          if (bm.search_case == MATCH)
  363.             i = strncmp( q, p, pl );
  364.          else
  365.             i = strnicmp( q, p, pl );
  366.          if (i == 0)
  367.             return( q );
  368.          i = 1;
  369.       }
  370.       s = cpf( s );
  371.    }
  372.  
  373.    /*
  374.     * haven't found pattern yet - now search from beginning of file
  375.     */
  376.    s = cpf( window->file_info->start_text );
  377.    search_length = ptoul( search_start ) + 1 - ptoul( s );
  378.    i = pl - 1;
  379.    for (search_length -= i; search_length >= 0; search_length -= i) {
  380.       s = s + i;
  381.       i = bm.skip_forward[*s];
  382.       if (i == 0) {
  383.          q = s + 1 - pl;
  384.          q = cpf( q );
  385.          if (bm.search_case == MATCH)
  386.             i = strncmp( q, p, pl );
  387.          else
  388.             i = strnicmp( q, p, pl );
  389.          if (i == 0)
  390.             return( q );
  391.          i = 1;
  392.       }
  393.       s = cpf( s );
  394.    }
  395.    /*
  396.     * pattern not found - return NULL pointer
  397.     */
  398.    return( NULL );
  399. }
  400.  
  401.  
  402. /*
  403.  * Name:    backward_boyer_moore_search
  404.  * Purpose: search backward for pattern using boyer array
  405.  * Passed:  window: information allowing access to current window etc
  406.  * Returns: position in file if found else return NULL
  407.  * Date:    June 5, 1991
  408.  * Notes:   Start searching from cursor position to beginning of file. If we
  409.  *          hit beginning end of file without a match, start searching from the
  410.  *          end of file to cursor position.  (do wrapped searches)
  411.  */
  412. text_ptr backward_boyer_moore_search( window )
  413. windows *window;
  414. {
  415. int i, pl;
  416. text_ptr p, s, search_start;
  417. long search_length;
  418.  
  419.    window->cursor = cpb( window->cursor );
  420.    search_start = window->cursor;
  421.    i = window->rcol - 1;
  422.    pl = linelen( window->cursor );
  423.    if (i > pl)
  424.       i = pl;
  425.    search_start += i;
  426.    s = search_start;
  427.    pl = bm.pattern_length;
  428.    i = -pl + 1;
  429.    search_length = ptoul( search_start ) + 1 -
  430.                    ptoul( window->file_info->start_text );
  431.    p = bm.pattern;
  432.    for (search_length += i; search_length >= 0; search_length += i) {
  433.       s = s + i;
  434.       i = bm.skip_backward[*s];
  435.       if (i == 0) {
  436.          if (bm.search_case == MATCH)
  437.             i = strncmp( s, p, pl );
  438.          else
  439.             i = strnicmp( s, p, pl );
  440.          if (i == 0)
  441.             return( s );
  442.          i = -1;
  443.       }
  444.       s = cpb( s );
  445.    }
  446.  
  447.    /*
  448.     * haven't found pattern yet - now search from end of file
  449.     */
  450.    window->file_info->end_text = cpb( window->file_info->end_text );
  451.    s = window->file_info->end_text - 1;
  452.    i = -pl + 1;
  453.    search_length = ptoul( s ) + 1 - ptoul( search_start );
  454.    for (search_length += i; search_length >= 0; search_length += i) {
  455.       s = s + i;
  456.       i = bm.skip_backward[*s];
  457.       if (i == 0) {
  458.          if (bm.search_case == MATCH)
  459.             i = strncmp( s, p, pl );
  460.          else
  461.             i = strnicmp( s, p, pl );
  462.          if (i == 0)
  463.             return( s );
  464.          i = -1;
  465.       }
  466.       s = cpb( s );
  467.    }
  468.  
  469.    /*
  470.     * pattern not found - return NULL pointer
  471.     */
  472.    return( NULL );
  473. }
  474.  
  475.  
  476. /*
  477.  * Name:    find_adjust
  478.  * Purpose: place cursor on screen given a posiont in file - default cline
  479.  * Passed:  window: information allowing access to current window etc
  480.  *          found:  position anywhere in file
  481.  * Date:    June 5, 1991
  482.  * Notes:   found could be anywhere in file.  Find the start of line that
  483.  *          found is on.  Determine if start of line is behind or ahead of
  484.  *          current line.  Once that is done, it is easy to determine if found
  485.  *          is on screen.  Lastly, current cursor position becomes start of
  486.  *          found line - reposition and display.
  487.  */
  488. void find_adjust( window, found )
  489. windows *window;
  490. text_ptr found;
  491. {
  492. int rcol;
  493. long pattern_line, rline, test_line;
  494. text_ptr p, q;
  495.  
  496.    /*
  497.     * find start of line found is on.
  498.     */
  499.    found = cpb( found );
  500.    if (*(found - 1) != '\n' && *(found - 1) != CONTROL_Z)
  501.       p = find_prev( found );
  502.    else
  503.       p = found;
  504.  
  505.    /*
  506.     * find real column found is on.
  507.     */
  508.    rcol = ptoul( found ) - ptoul( p );
  509.    rline = pattern_line = window->rline;
  510.    q = window->cursor = cpf( window->cursor );
  511.  
  512.    /*
  513.     * if p, start of found line, is greater than current line, see if
  514.     * p is between current line and bottom line on screen.
  515.     */
  516.    if (ptoul( q ) < ptoul( p )) {
  517.       while (ptoul( q ) != ptoul( p )) {
  518.          q = find_next( q );
  519.          ++pattern_line;
  520.       }
  521.       test_line = pattern_line - rline;
  522.       if ((long)window->cline + test_line <= (long)window->bottom_line)
  523.          window->cline += test_line;
  524.       else
  525.          window->dirty = LOCAL;
  526.  
  527.    /*
  528.     * if p, start of found line, is less than current line, see if
  529.     * p is between current line and top line on screen.
  530.     */
  531.    } else if (ptoul( q ) > ptoul( p )) {
  532.       q = cpb( q );
  533.       while (ptoul( q ) != ptoul( p )) {
  534.          q = find_prev( q );
  535.          --pattern_line;
  536.       }
  537.       test_line = rline - pattern_line;
  538.       if ((long)window->cline - test_line > (long)(window->top_line-1))
  539.          window->cline -= test_line;
  540.       else
  541.          window->dirty = LOCAL;
  542.       if (pattern_line < (long)(window->cline - (window->top_line-1)))
  543.          window->cline = pattern_line + window->top_line - 1;
  544.    }
  545.  
  546.    /*
  547.     * cursor line becomes found line.  check if column is on screen.
  548.     */
  549.    window->cursor = p;
  550.    window->rline = pattern_line;
  551.    check_virtual_col( window, rcol, rcol );
  552. }
  553.  
  554.  
  555. /*
  556.  * Name:    replace_string
  557.  * Purpose: To set up and perform a replace operation.
  558.  * Date:    June 5, 1991
  559.  * Passed:  window: information allowing access to current window etc
  560.  *          direction:  replace forward or backward in file
  561.  */
  562. void replace_string( window, direction )
  563. windows *window;
  564. int direction;
  565. {
  566. char pattern[MAX_COLS];  /* the old and replacement text */
  567. int prompt_line;
  568. int net_change;
  569. int sub_len;
  570. int file_changed;
  571. int finished;
  572. int rc;
  573. text_ptr pattern_location;
  574. text_ptr replace_start;
  575. unsigned long rs, pl;
  576. windows wp;
  577. int color;
  578. char *snf = "string not found";
  579.  
  580.    prompt_line = window->bottom_line;
  581.    color = g_display.message_color;
  582.    /*
  583.     * get the old text, using the previous as the default
  584.     */
  585.    strcpy( pattern, g_status.pattern );
  586.    if (get_name( "String to find: ", prompt_line, pattern, color ) != OK)
  587.       return;
  588.    strcpy( g_status.pattern, pattern );
  589.  
  590.    /*
  591.     * get the replacement text, using the previous as the default
  592.     */
  593.    strcpy( pattern, g_status.subst );
  594.    if (get_name( "Replacement:    ", prompt_line, pattern, color ) != OK)
  595.       return;
  596.    strcpy( g_status.subst, pattern );
  597.    sub_len = strlen( pattern );
  598.    strcpy( bm.pattern, g_status.pattern );
  599.    net_change = sub_len - strlen( g_status.pattern );
  600.  
  601.    /*
  602.     * get the replace flags
  603.     */
  604.    if (get_replacement_flags( prompt_line ) != OK)
  605.       return;
  606.  
  607.    build_boyer_array( );
  608.    dup_window_info( &wp, window );
  609.  
  610.    finished = FALSE;
  611.    file_changed = FALSE;
  612.    if (direction == FORWARD) {
  613.       if ((replace_start = forward_boyer_moore_search( &wp )) != NULL) {
  614.          rs = ptoul( replace_start );
  615.          replace_and_display( &wp, replace_start, &finished, &file_changed,
  616.                               direction );
  617.       } else {
  618.          error( WARNING, prompt_line, snf );
  619.          finished = TRUE;
  620.       }
  621.       while (finished == FALSE) {
  622.          if ((pattern_location = forward_boyer_moore_search( &wp )) != NULL) {
  623.             pl = ptoul( pattern_location );
  624.             if (rs == pl)
  625.                finished = TRUE;
  626.             else {
  627.                rc = replace_and_display( &wp, pattern_location, &finished,
  628.                                  &file_changed, direction );
  629.                if (rc == OK && rs > pl)
  630.                   rs += net_change;
  631.             }
  632.          } else {
  633.             error( WARNING, prompt_line, snf );
  634.             finished = TRUE;
  635.          }
  636.       }
  637.    } else {
  638.       if ((replace_start = backward_boyer_moore_search( &wp )) != NULL) {
  639.          rs = ptoul( replace_start );
  640.          replace_and_display( &wp, replace_start, &finished, &file_changed,
  641.                               direction );
  642.       } else {
  643.          error( WARNING, prompt_line, snf );
  644.          finished = TRUE;
  645.       }
  646.       while (finished == FALSE) {
  647.          if ((pattern_location = backward_boyer_moore_search( &wp )) != NULL) {
  648.             pl = ptoul( pattern_location );
  649.             if (rs == pl || (pl > rs && rs > pl - sub_len))
  650.                finished = TRUE;
  651.             else {
  652.                rc = replace_and_display( &wp, pattern_location, &finished,
  653.                                  &file_changed, direction );
  654.                if (rc == OK && rs > pl)
  655.                   rs += net_change;
  656.             }
  657.          } else {
  658.             finished = TRUE;
  659.             error( WARNING, prompt_line, snf );
  660.          }
  661.       }
  662.    }
  663.    dup_window_info( window, &wp );
  664.    if (file_changed)
  665.       window->file_info->modified = TRUE;
  666. }
  667.  
  668.  
  669. /*
  670.  * Name:    replace_and_display
  671.  * Purpose: To make room for replacement string
  672.  * Date:    June 5, 1991
  673.  * Passed:  window: information allowing access to current window etc
  674.  *          pattern_location:  pointer to position of pattern in file
  675.  *          finished:  boolean - stop replacing on this occurrence
  676.  *          modified:  boolean - user may decide to skip this replacement
  677.  * Notes:   Show user where pattern_location is on screen if needed.
  678.  *          Replace and display if needed.
  679.  */
  680. int  replace_and_display( window, pattern_location, finished, modified,
  681.                           direction )
  682. windows *window;
  683. text_ptr pattern_location;
  684. int *finished, *modified;
  685. int direction;
  686. {
  687. int rc, ask_user;
  688.  
  689.    ask_user = g_status.replace_flag;
  690.    find_adjust( window, pattern_location );
  691.    xygoto( window->ccol, window->cline );
  692.    if (window->dirty) {
  693.       display_current_window( window );
  694.       window->dirty = FALSE;
  695.    }
  696.    rc = OK;
  697.    if (ask_user == PROMPT) {
  698.       show_line_col( window );
  699.       rc = ask_replace( window, finished );
  700.    }
  701.    if (rc == OK) {
  702.       do_replace( window, pattern_location, direction );
  703.       *modified = TRUE;
  704.       window->dirty = GLOBAL;
  705.       if (ask_user == PROMPT)
  706.          show_changed_line( window );
  707.    }
  708.    return( rc );
  709. }
  710.  
  711.  
  712. /*
  713.  * Name:    goto_top_file
  714.  * Purpose: To move the cursor to the top of the file.
  715.  * Date:    June 5, 1991
  716.  * Passed:  window: information allowing access to current window etc
  717.  */
  718. void goto_top_file( window )
  719. windows *window;
  720. {
  721. text_ptr next;      /* successive lines above the cursor */
  722. int i;
  723.  
  724.    if (window->rline != window->cline - (window->top_line-1)) {
  725.       next = cpf( window->file_info->start_text );
  726.       for (i=window->cline; i>window->top_line; i--)
  727.          next = find_next( next );
  728.       window->cursor = next;
  729.       window->rline = window->cline - (window->top_line-1);
  730.       display_current_window( window );
  731.    }
  732. }
  733.  
  734. /*
  735.  * Name:    goto_end_file
  736.  * Purpose: To move the cursor to the end of the file.
  737.  * Date:    June 5, 1991
  738.  * Passed:  window: information allowing access to current window etc
  739.  */
  740. void goto_end_file( window )
  741. windows *window;
  742. {
  743. text_ptr prev;
  744. int i;
  745. int vir_row;
  746.  
  747.    prev = cpb( window->file_info->end_text ) - 1;
  748.    for (i=window->page; i>=0 && prev != NULL; i--)
  749.       prev = find_prev( prev );
  750.    if (prev != NULL && ptoul( prev ) > ptoul( window->cursor )) {
  751.       vir_row = window->bottom_line - window->cline;
  752.       window->rline = window->file_info->length - vir_row;
  753.       prev = cpf( prev );
  754.       for (i=window->top_line; i<window->cline; i++)
  755.          prev = find_next( prev );
  756.       window->cursor = prev;
  757.       display_current_window( window );
  758.    }
  759. }
  760.  
  761. /*
  762.  * Name:    scan_forward
  763.  * Purpose: To find the corresponding occurrence of target, ignoring
  764.  *           embedded pairs of opp and target, searching forwards.
  765.  * Date:    June 5, 1991
  766.  * Passed:  start:  position of character to be paired
  767.  *          opp:    the opposite to target, if any
  768.  *          target: the string to be found
  769.  * Returns: the location of the corresponding target in the text buffer
  770.  * Notes:   Every 8,000 characters, check pointer forward.
  771.  */
  772. text_ptr scan_forward( start, opp, target )
  773. text_ptr start;
  774. char opp;
  775. char target;
  776. {
  777. text_ptr orig;
  778. int count = 0;  /* number of unmatched opposites found */
  779. int check = 0;
  780. char c;
  781.  
  782.    orig = start = cpf( start );
  783.    while ((c = *++start) && (c != CONTROL_Z)) {
  784.       check++;
  785.       if (opp == c)
  786.          count++;
  787.       else if (target == c) {
  788.          if (count == 0)
  789.             break;
  790.          --count;
  791.       }
  792.       if (check > 8000) {
  793.          start = cpf( start );
  794.          check = 0;
  795.       }
  796.    }
  797.    if (c == CONTROL_Z)
  798.       start = orig;
  799.    return( start );
  800. }
  801.  
  802. /*
  803.  * Name:    scan_backward
  804.  * Purpose: To find the corresponding occurrence of target, ignoring
  805.  *           embedded pairs of opp and target, searching backwards.
  806.  * Date:    June 5, 1991
  807.  * Passed:  start:  position of character to be paired
  808.  *          opp:    the opposite to target, if any
  809.  *          target: the string to be found
  810.  * Returns: the location of the corresponding target in the text buffer
  811.  */
  812. text_ptr scan_backward( start, opp, target )
  813. text_ptr start;
  814. char opp;
  815. char target;
  816. {
  817. text_ptr orig;
  818. int count = 0;  /* number of unmatched opposites found */
  819. int check = 0;
  820. char c;
  821.  
  822.    orig = start = cpb( start );
  823.    while ((c = *--start) && (c != CONTROL_Z)) {
  824.       check++;
  825.       if (opp == c)
  826.          count++;
  827.       else if (target == c) {
  828.          if (count == 0)
  829.             break;
  830.          --count;
  831.       }
  832.       if (check > 8000) {
  833.          start = cpb( start );
  834.          check = 0;
  835.       }
  836.    }
  837.    if (c == CONTROL_Z)
  838.       start = orig;
  839.    return( start );
  840. }
  841.  
  842. /*
  843.  * Name:    match_pair
  844.  * Purpose: To find the corresponding pair to the character under the
  845.  *           cursor.
  846.  * Date:    June 5, 1991
  847.  * Passed:  window:   information allowing access to current window etc
  848.  * Notes:   Searching is very simple-minded, and does not cope with things
  849.  *          like brackets embedded within quoted strings.
  850.  */
  851. void match_pair( window )
  852. windows *window;
  853. {
  854. text_ptr orig;  /* cursor location in text */
  855. int c;
  856.  
  857.    /*
  858.     * make sure the character under the cursor is one that has a
  859.     *  matched pair
  860.     */
  861.    if (window->rcol >= linelen( window->cursor ))
  862.       return;
  863.    window->cursor = cpf( window->cursor );
  864.    orig = window->cursor + window->rcol;
  865.    c = *orig;
  866.    if (strchr( "[]{}()", c ) == NULL)
  867.       return;
  868.  
  869.    /*
  870.     * find the matching pair
  871.     */
  872.    switch (c) {
  873.    case '[':
  874.       orig = scan_forward( orig, '[', ']' );
  875.       break;
  876.    case '(':
  877.       orig = scan_forward( orig, '(', ')' );
  878.       break;
  879.    case '{':
  880.       orig = scan_forward( orig, '{', '}' );
  881.       break;
  882.    case ']':
  883.       orig = scan_backward( orig, ']', '[' );
  884.       break;
  885.    case ')':
  886.       orig = scan_backward( orig, ')', '(' );
  887.       break;
  888.    case '}':
  889.       orig = scan_backward( orig, '}', '{' );
  890.       break;
  891.    }
  892.  
  893.    /*
  894.     * now show the user what we have found
  895.     */
  896.    find_adjust( window, orig );
  897. }
  898.  
  899. /*
  900.  * Name:    goto_line
  901.  * Purpose: To move the cursor to a particular line in the file
  902.  * Date:    June 5, 1991
  903.  * Passed:  window: information allowing access to current window etc
  904.  */
  905. void goto_line( window )
  906. windows *window;
  907. {
  908. long number;             /* line number selected */
  909. long i;                  /* line counter */
  910. char num_str[MAX_COLS];  /* line number as string */
  911. text_ptr p;              /* used to scan through file counting lines */
  912. int prompt_line;
  913.  
  914.    prompt_line = window->bottom_line;
  915.    /*
  916.     * find out where we are going
  917.     */
  918.    num_str[0] = '\0';
  919.    if (get_name( "Line number: ", prompt_line, num_str,
  920.                  g_display.message_color ) != OK)
  921.       return;
  922.    number = atol( num_str );
  923.  
  924.    if (number > 0  && number <= window->file_info->length) {
  925.       p = window->cursor;
  926.       i = window->rline;
  927.       if (number < window->rline) {
  928.          p = cpb( p );
  929.          for (; i>number; i--)
  930.             p = find_prev( p );
  931.       } else {
  932.          cpf( p );
  933.          for (; i<number; i++)
  934.             p = find_next( p );
  935.       }
  936.       find_adjust( window, p );
  937.    } else {
  938.       strcat( num_str, "must be in the range 1 - " );
  939.       ltoa( window->file_info->length, num_str+25, 10 );
  940.       error( WARNING, prompt_line, num_str );
  941.    }
  942. }
  943.